#ifndef _SERVICE_EX_H_
#define _SERVICE_EX_H_

#include "IPacketParser.h"
#include "BaseService.h"
#include "Timer.h"
#include "LogEx.h"
#include "EventWorkerEx.h"
#include "IoServiceEx.h"
#include "SetupSocket.h"
#ifdef _USE_CURL_
#include "CurlManager.h"
typedef void (*postRspCallback)(const std::string &rsp, void *data, s32 len);
#endif

class IServiceEx : public SGLib::IEventEx
{
public:
	virtual ~IServiceEx(){}
	virtual bool Start() = 0;
	virtual void Stop() = 0;
	virtual void Connect(const char *ip, s32 port, UserData *ud) = 0;
    virtual s32 AddTimer(u32 elapse, SGLib::CallBackFunc func, void *data, s32 len, bool isPersist) = 0;
    virtual void DelTimer(s32 timerId) = 0;
	virtual SGLib::IPacketParser* GetPacketParser() = 0;
	virtual SGLib::CIoServiceExPool& GetIoServicePool() = 0;
    virtual void DoWork(SGLib::CallBackFunc func, void *data, s32 len) = 0;
#ifdef _USE_CURL_
    virtual void PostRequest(const std::string &url, const std::string &postdata, s32 timeOut, postRspCallback rspfunc, void *data, s32 len) = 0;
#endif
		
	virtual void HandleEvent(s32 paramLen, char *paramData){}
};

template<typename TSession, typename TRpcSession, typename TPacketParser, s32 TimerCount>
class CServiceEx : public IServiceEx 
{
public:
	struct CallBackHead
	{
		SGLib::CallBackFunc func;
		s32 len;
		void *obj;
	};
	enum
	{
		E_Timer_DataLen = SGLib::CEventWorkerEx::E_Max_SessionEvent_ParamSize - sizeof(CallBackHead) - sizeof(void*),
	};
	struct CallBackData
	{
		CallBackHead head;
		char data[E_Timer_DataLen];
	};

public:
	// 固定使用单线程worker模式
    CServiceEx(s32 port, s32 connCount, s32 szSend, s32 szRecv, s32 connRpcCount, s32 szRpcSend, s32 szRpcRecv, s32 ioCount, s32 szMaxEvent) : 
		m_port(port),
		m_timer(),
		m_service(
			m_parser, 
			connCount, szSend, szRecv,
			connRpcCount, szRpcSend, szRpcRecv,
			ioCount, true, 1, szMaxEvent)
    {
        SGLib::CSetupSocket::Instance().IgnoreSignal();
    }
	
	virtual ~CServiceEx()
	{
	}

    virtual bool Start()
    {
		return ( m_timer.Start() && m_service.Start(m_port) );
    }

    virtual void Stop()
    {
		m_timer.Stop();
		m_service.Stop();
    }

    virtual void Connect(const char *ip, s32 port, UserData *ud)
    {
		m_service.Connect( ip, port, ud );
    }
	
	virtual void HandleEvent(s32 len, char *data)
	{
		// (1) timer work here
        // (2) DoWork also here
#if 0 
		LOG_DEBUG( "ServiceEx::HandleEvent proc timer event" );
#endif
		SELF_ASSERT( data!=NULL && len>0, return; );
		CallBackData *cbData = (CallBackData*)data;
		cbData->head.func( cbData->data, cbData->head.len );
	}

    virtual s32 AddTimer(u32 elapse, SGLib::CallBackFunc func, void *data, s32 len, bool isPersist)
	{
		if( len > E_Timer_DataLen )
		{
            LOG_ERROR( "[%s][%s][%d]", __FILE__, __FUNCTION__, __LINE__ );
			return SGLib::CTimer<TimerCount>::E_Invalid_TimerId;
		}

        IServiceEx *service = this;
		CallBackData cbData;
		cbData.head.obj = service;
		cbData.head.func = func;
		cbData.head.len = len;
		if( cbData.head.len > 0 )
		{
			memcpy( cbData.data, data, cbData.head.len );
		}

		return m_timer.AddTimer( elapse, _TimerCallBackFunc, &cbData, sizeof(cbData), isPersist );
	}

    virtual void DelTimer(s32 timerId)
	{
		m_timer.DelTimer( timerId );
	}

	virtual SGLib::IPacketParser* GetPacketParser()
	{
		return &m_parser;
	}

	virtual SGLib::CIoServiceExPool& GetIoServicePool()
	{
		return m_service.GetIoServiceExPool();
	}
    
    virtual void DoWork(SGLib::CallBackFunc func, void *data, s32 len)
    {
        if( len > E_Timer_DataLen )
		{
            LOG_ERROR( "[%s][%s][%d] len[%d - %d]", __FILE__, __FUNCTION__, __LINE__,
                len, E_Timer_DataLen );
			return;
		}

        CallBackData cbData;
		cbData.head.obj = this;
		cbData.head.func = func;
		cbData.head.len = len;
		if( cbData.head.len > 0 )
		{
			memcpy( cbData.data, data, cbData.head.len );
		}
        SGLib::CIoServiceEx *io = GetIoServicePool().GetIoService();
        SELF_ASSERT( io, return; );
        if( !io->TryPushEvent( 0, sizeof(cbData), &cbData, this) )
        {
            LOG_ERROR( "[%s][%s][%d]", __FILE__, __FUNCTION__, __LINE__ );
        }
    }

#ifdef _USE_CURL_
    struct CurlData
    {
        void *obj;
        postRspCallback func;
		s32 len;
        std::string *rsp;
		char data[E_Timer_DataLen - 32];
    };
    virtual void PostRequest(const std::string &url, const std::string &postdata, s32 timeOut, postRspCallback rspfunc, void *data, s32 len) 
    {
        if( len > E_Timer_DataLen )
        {
            return;
        }
        CurlData cd;
        cd.obj = this;
		cd.func = rspfunc;
		cd.len = len;
        cd.rsp = NULL;
		if( cd.len > 0 )
		{
			memcpy( cd.data, data, cd.len );
		}
        std::string _ud;
        _ud.append( (char*)&cd, sizeof(cd) );
        CCurlManager::Instance().PostRequest4Data( url, postdata, NULL, NULL, _curl_OnDataEnd, _ud, timeOut );
    }
#endif

private:
    static void _TimerCallBackFunc(void *data, s32 len)
    {
        SELF_ASSERT( data!=NULL && len>0, return; );
        CallBackData *cbData = (CallBackData*)data;
        IServiceEx *service = (IServiceEx*)(cbData->head.obj);
        SELF_ASSERT( service, return; );
        // 单eventworker线程模式只有一个eventWorker
        // 所有IoServiceEx都指向同一个eventWorker
        SGLib::CIoServiceEx *io = service->GetIoServicePool().GetIoService();
        SELF_ASSERT( io && service, return; );
        if( !io->TryPushEvent( 0, len, data, service ) )
        {
            LOG_ERROR( "[%s][%s][%d]", __FILE__, __FUNCTION__, __LINE__ );
        }
    }

#ifdef _USE_CURL_
    static void _curl_OnDataEnd(const std::string &userdata, const std::string &rsp)
    {
        // in curl thread, push to event thread
        LOG_DEBUG( "[%s][%s][%d] rsp[%s]", __FILE__, __FUNCTION__, __LINE__, rsp.c_str() );
        CurlData *cd = (CurlData*)userdata.c_str();
        cd->rsp = new std::string( rsp );
        IServiceEx *service = (IServiceEx*)(cd->obj);
        service->DoWork( _curl_DataCallback, cd, userdata.length() );
    }
    static void _curl_DataCallback(void *data, s32 len)
    {
        CurlData *cd = (CurlData*)data;
        LOG_DEBUG( "[%s][%s][%d] rsp[%s]", __FILE__, __FUNCTION__, __LINE__, cd->rsp->c_str() );
        cd->func( *(cd->rsp), cd->data, cd->len );
        delete cd->rsp;
    }
#endif

private:
	s32 m_port;
	TPacketParser m_parser;
	SGLib::CTimer<TimerCount> m_timer;
	CBaseService<TSession, TRpcSession, TPacketParser> m_service;
};

class CServiceExManager
{
    SINGLETON_CLASS_DEFINE(CServiceExManager);
public:
	template<typename TSession, typename TRpcSession, typename TPacketParser, s32 TimerCount>
	IServiceEx* CreateServiceEx(s32 port, s32 connCount, s32 szSend, s32 szRecv, s32 connRpcCount, s32 szRpcSend, s32 szRpcRecv, s32 ioCount, s32 szMaxEvent)
	{
		m_service = new CServiceEx<TSession, TRpcSession, TPacketParser, TimerCount>(
            port,
            connCount, szSend, szRecv,
            connRpcCount, szRpcSend, szRpcRecv,
            ioCount, szMaxEvent );
		return m_service;
	}

	void DestoryServiceEx()
    {
        if( m_service )
        {
            delete m_service;
            m_service = NULL;
        }
    }

	IServiceEx* GetServiceEx()
    {
	    return m_service;
    }

private:
	IServiceEx *m_service;
};

template<typename TSession, typename TRpcSession, typename TPacketParser, s32 TimerCount, s32 CurlCount>
bool StartServiceEx(s32 port, s32 connCount, s32 szSend, s32 szRecv, s32 connRpcCount, s32 szRpcSend, s32 szRpcRecv, s32 ioCount, s32 szMaxEvent)
{
	IServiceEx *service = CServiceExManager::Instance().CreateServiceEx<TSession, TRpcSession, TPacketParser, TimerCount>(
        port,
        connCount, szSend, szRecv,
        connRpcCount, szRpcSend, szRpcRecv,
        ioCount, szMaxEvent );
	if( service && service->Start() ) 
	{
#ifdef _USE_CURL_
        CCurlManager::Instance().Init( CurlCount );
        if( !CCurlManager::Instance().Start() )
        {
            LOG_ERROR( "CCurlManager start failed" );
            return false;
        }
        else
        {
            LOG_INFO( "CCurlManager start" );
        }
#else
        //CCurlManager::Instance().Init( 0 );
#endif

        LOG_INFO( "[%s][%s][%d] Success.", __FILE__, __FUNCTION__, __LINE__ );
		return true;
	}

    LOG_ERROR( "[%s][%s][%d]", __FILE__, __FUNCTION__, __LINE__ );
	return false;
}


template<s32 none>
void StopServiceEx()
{
	IServiceEx *service = CServiceExManager::Instance().GetServiceEx();
	if( service )
	{
		service->Stop();
#ifdef _USE_CURL_
		CCurlManager::Instance().Stop();
#endif
	}
	CServiceExManager::Instance().DestoryServiceEx();
    LOG_INFO( "[%s][%s][%d]", __FILE__, __FUNCTION__, __LINE__ );
}

#endif

